﻿
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;

namespace SuperTracker
{
    using System.Collections;

    class Program
    {
        static void Main(string[] args)
        {
            Trace.Listeners.Add(new ConsoleTraceListener());

            var shipClassIds = new int[]{ 3514,22852,23913,23917,23919,671,3764,11567,23773 };

            var startDate = new DateTime(2012, 06, 01);
            var endDate = new DateTime(2012, 06, 05);

            var kills = GetKillmails(startDate, endDate);
            var count = kills.Count();

            Trace.WriteLine(string.Format("{0} retrieved kills",count));

            var records = new ArrayList();
            foreach (var kill in kills)
            {
                foreach (var involved in kill.Involved.Where(i => shipClassIds.Contains(i.ShipId)))
                {
                    var record = new { Date = kill.Date, System = kill.SystemName, Name = involved.CharacterName, Corporation = involved.CorporationName, Alliance = involved.AllianceName };
                    records.Add(record);
                }
            }
            Trace.WriteLine(string.Format("Loaded {0} records", records.Count));

        }

        private const int MAX_LIMIT = 500;

        static IEnumerable<FeedKillmail> GetKillmails(DateTime? startDate, DateTime? endDate, uint max = MAX_LIMIT)
        {
            var supers = new string[]{ "Revenant","Hel","Nyx","Wyvern","Aeon","Erebus","Leviathan","Avatar","Ragnarok" };

            var result = new List<FeedKillmail>();
            try
            {
                if (max > MAX_LIMIT)
                    max = MAX_LIMIT;

                bool hasDates = startDate.HasValue && endDate.HasValue;
                
                for(int i = 0; i < supers.Length; i++)
                {
                    Trace.WriteLine(string.Format("Retrieving kills with involved {0}", supers[i]));

                    string uri = Uri.EscapeUriString(string.Format("http://eve-kill.net/epic/involvedShip:{0}/mask:557062/mailLimit:{1}/{2}", supers[i], max, hasDates ? string.Format("startDate:{0:yyyy-MM-dd}/endDate:{1:yyyy-MM-dd}", startDate.Value, endDate.Value) : null));

                    string content = GetContent(uri);
                    if (string.IsNullOrEmpty(content) || string.Equals("[]", content)) continue;

                    var kills = DeserializeContent(content);
                    if (kills != null && kills.Any())
                    {
                        result.AddRange(kills.Where(k => !result.Contains(k)));

                        if (hasDates)
                            while (kills.Count == max)
                            {
                                uri = Uri.EscapeUriString(string.Format("http://eve-kill.net/epic/involvedShip:{0}/mask:557062/mailLimit:{1}/startDate:{2:yyyy-MM-dd}/endDate:{3:yyyy-MM-dd}", supers[i], max, startDate, kills.Min(k => k.Date)));
                                var temp = GetContent(uri);
                                if (string.IsNullOrEmpty(temp) || string.Equals("[]", temp)) break;

                                kills = DeserializeContent(temp);
                                if (kills == null) break;
                                result.AddRange(kills.Where(k => !result.Contains(k)));
                            }
                    }
                }
                
            }
            catch (Exception exp)
            {
                Trace.TraceError("The attempt to get killmails has thrown an exception of type \"{0}\". Details: {1}", exp.GetType(), exp.Message);
            }
            
            return result;
        }

        private static string GetContent(string uri)
        {
            if (string.IsNullOrEmpty(uri)) throw new ArgumentNullException("uri");
            try
            {
                var request = (HttpWebRequest)WebRequest.Create(uri);
                request.KeepAlive = false;

                var response = request.GetResponse();
                var st = response.GetResponseStream();
                if (st == null) return null;

                using (st)
                using (var reader = new StreamReader(st))
                    return reader.ReadToEnd();
            }
            catch (Exception exp)
            {
                Trace.TraceError("The http request with uri \"{2}\" has thrown an exception of type \"{0}\". Details: {1}", exp.GetType(), exp.Message, uri);
                return null;
            }
        }
        private static List<FeedKillmail> DeserializeContent(string content)
        {
            if (string.IsNullOrEmpty(content)) throw new ArgumentNullException("content");
            try
            {
                var ser =  JsonSerializer.Create(new JsonSerializerSettings() { DefaultValueHandling = DefaultValueHandling.Ignore, MissingMemberHandling = MissingMemberHandling.Ignore });
                ser.Converters.Add(new FeedKillmailInvolvedListConverter());
                
                using (var jreader = new JsonTextReader(new StringReader(content)))
                    return ser.Deserialize(jreader, typeof(List<FeedKillmail>)) as List<FeedKillmail>;
            }
            catch (Exception exp)
            {
                Trace.TraceError("The attempt to deserialize content \"{2}\" has thrown an exception of type \"{0}\". Details: {1}", exp.GetType(), exp.Message, content);
                return null;
            }
        }
        private class FeedKillmailInvolvedListConverter : CustomCreationConverter<IEnumerable<FeedKillmailInvolved>>
        {
            public override IEnumerable<FeedKillmailInvolved> Create(Type objectType)
            {
                return new List<FeedKillmailInvolved>();
            }
        }

        [JsonObjectAttribute(MemberSerialization.OptIn)]
        public class FeedKillmail
        {
            [JsonProperty(PropertyName = "timestamp")]
            public DateTime Date { get; set; }
            [JsonProperty(PropertyName = "internalID")]
            public int InternalId { get; set; }
            [JsonProperty(PropertyName = "solarSystemName")]
            public string SystemName { get; set; }
            [JsonProperty(PropertyName = "involved")]
            public IEnumerable<FeedKillmailInvolved> Involved { get; set; }

            public bool Equals(FeedKillmail other)
            {
                if (ReferenceEquals(null, other)) return false;
                if (ReferenceEquals(this, other)) return true;
                return other.InternalId == InternalId;
            }

            public override bool Equals(object obj)
            {
                if (ReferenceEquals(null, obj)) return false;
                if (ReferenceEquals(this, obj)) return true;
                if (obj.GetType() != typeof (FeedKillmail)) return false;
                return Equals((FeedKillmail) obj);
            }

            public override int GetHashCode()
            {
                return InternalId;
            }
        }
        [JsonObjectAttribute(MemberSerialization.OptIn)]
        public class FeedKillmailInvolved
        {
            [JsonProperty(PropertyName = "characterID")]
            public int CharacterId { get; set; }
            [JsonProperty(PropertyName = "characterName")]
            public string CharacterName { get; set; }
            [JsonProperty(PropertyName = "corporationID")]
            public int CorporationId { get; set; }
            [JsonProperty(PropertyName = "corporationName")]
            public string CorporationName { get; set; }
            [JsonProperty(PropertyName = "allianceID")]
            public int AllianceId { get; set; }
            [JsonProperty(PropertyName = "allianceName")]
            public string AllianceName { get; set; }
            [JsonProperty(PropertyName = "factionID")]
            public int FactionId { get; set; }
            [JsonProperty(PropertyName = "factionName")]
            public string FactionName { get; set; }
            [JsonProperty(PropertyName = "securityStatus")]
            public float SecurityStatus { get; set; }
            [JsonProperty(PropertyName = "damageDone")]
            public int DamageDone { get; set; }
            [JsonProperty(PropertyName = "finalBlow")]
            public bool IsFinalBlow { get; set; }
            [JsonProperty(PropertyName = "shipTypeID")]
            public int ShipId { get; set; }
            [JsonProperty(PropertyName = "weaponTypeID")]
            public int WeaponId { get; set; }
        }
    }
    
}
